//Библиотека OpenCV.
#include <opencv/cv.h>


//Библиотека Qt.
#include <QDateTime>
#include <QTimer>


//Внутренняя библиотека.
#include "viddevice.h"
#include "vidstream.h"
#include "filewriter.h"
#include "vidcodec.h"
#include "vidframe.h"


//===========================================================================//
//=Заголовок: VidDevice::VidDevice() - public
//=Параметры:
//=Описание: Конструктор.
//===========================================================================//
VidDevice::VidDevice(int index, QObject *parent) : QThread(parent)
{
devIndex = index;

baseName = "device" + QString::number(index);

vidStream = NULL;

//Активировать ДД.
/*isMotion = false;
motion = new Motion();
connect(motion, SIGNAL(motionDetect()), this, SLOT(slotModeTriggered()), Qt::QueuedConnection);
connect(motion, SIGNAL(achievedTreshold(bool)), this, SIGNAL(achievedTreshold(bool)), Qt::QueuedConnection);*/

//Установить индекс видеокодека по умолчанию.
vCodecIndex = VidCodec::getIndexFromID(CODEC_ID_FLV1);

//Активировать запись в файл.
fileWriter = new FileWriter(baseName);

stopWriteTimer = new QTimer();
stopWriteTimer->setSingleShot(true);
stopWriteTimer->setInterval(30000);
connect(stopWriteTimer, SIGNAL(timeout()), this, SLOT(slotModeTriggered()), Qt::QueuedConnection);
}


//===========================================================================//
//=Заголовок: VidDevice::~VidDevice() - public
//=Параметры:
//=Описание: Деструктор.
//===========================================================================//
VidDevice::~VidDevice()
{
mutex.lock();
if(stopWriteTimer) delete stopWriteTimer;
if(fileWriter)
	{
	fileWriter->quit();
	while(fileWriter->isRunning()) {}
	delete fileWriter;
}
if(vidStream)
	{
	if(vidStream->isRunning()) vidStream->quit();
	while(vidStream->isRunning()) {}
	delete vidStream;
}
mutex.unlock();
wait();
}


//===========================================================================//
//=Заголовок: QString VidDevice::typeToString() - public
//=Параметры:
//=Описание: Функция конвертации типа устройства в строку.
//===========================================================================//
QString VidDevice::typeToString(VidDevice::DEVTYPE devtype)
{
QString typestr;
switch(devtype)
	{
	case VIDEO4LINUX2: typestr = "VIDEO4LINUX2"; break;
	case FILEREADER:   typestr = "FILEREADER";   break;
}
return typestr;
}


//===========================================================================//
//=Заголовок: int VidDevice::getIndex() - public
//=Параметры:
//=Описание: Функция возврата индекса устройства.
//===========================================================================//
int VidDevice::getIndex() {return devIndex;}


//===========================================================================//
//=Заголовок: void VidDevice::setBaseName() - public
//=Параметры:
//=Описание: Функция установки базового имени устройства.
//===========================================================================//
void VidDevice::setBaseName(QString name)
{
mutex.lock(); baseName = name; mutex.unlock();
if(fileWriter) fileWriter->setDeviceName(baseName);
}


//===========================================================================//
//=Заголовок: QString VidDevice::getBaseName() - public
//=Параметры:
//=Описание: Функция возврата базового имени устройства.
//===========================================================================//
QString VidDevice::getBaseName() {return baseName;}


//===========================================================================//
//=Заголовок: bool VidDevice::getListParam() - public
//=Параметры:
//=Описание: Функция возврата списка значений параметра фильтра.
//===========================================================================//
bool VidDevice::getListParam(DevParam::GET_LIST_PARAM param, QHash<int, QVariant> *valhash)
{
switch(param)
	{
	case DevParam::GET_LIST_FILEWRITER_FORMATS:
		VidCodec::enumSupportedFormats(vCodecIndex, valhash);
	break;
	case DevParam::GET_LIST_VIDEOCODEC_NAMES: {
		QVariant v4l2pixfmt;
		if(!getParam(DevParam::GET_PIXFORMAT, &v4l2pixfmt)) return false;
		VidCodec::enumSupportedCodecs(VidFrame::convertV4L2ToFFMPEGPixFormat(v4l2pixfmt.toInt()), valhash);
	} break;
	default: return false;
}

return true;
}


//===========================================================================//
//=Заголовок: bool VidDevice::getParam() - public
//=Параметры:
//=Описание: Функция возврата параметра устройства.
//===========================================================================//
bool VidDevice::getParam(DevParam::GET_PARAM param, QVariant *value)
{
switch(param)
	{
	case DevParam::GET_VIDEOWINDOW_CONNECTED:
		vidStream->getParam(param, value);
	break;
	case DevParam::GET_MOTION_CONNECTED:
		vidStream->getParam(param, value);
	break;
	/*case DevParam::GET_MOTION_ENABLED:
		(*value) = isMotion;
	break;
	case DevParam::GET_MOTION_TRESHOLD:
		(*value) = motion->getTreshold();
	break;
	case DevParam::GET_MOTION_SENSITIVE:
		(*value) = motion->getSensitive();
	break;
	case DevParam::GET_MOTION_NOISEREMOVING:
		(*value) = motion->getNoiseRemoving();
	break;
	case DevParam::GET_VIDEOCODEC_CURRENT:
		(*value) = vCodecIndex;
	break;
	case DevParam::GET_FILEWRITER_ENABLED:
		if(isMotion) {(*value) = false; break;}
		if(devMode == FILEWRITING) (*value) = true;
		else (*value) = false;
	break;*/
	default: return false;
}

return true;
}


//===========================================================================//
//=Заголовок: void VidDevice::slotSetParam() - public slots
//=Параметры:
//=Описание: Слот установки параметра устройства.
//===========================================================================//
void VidDevice::slotSetParam(DevParam::SET_PARAM /*param*/, int /*value*/)
{
/*switch(param)
	{
	case DevParam::SET_MOTION_TRESHOLD:    motion->setTreshold(value); break;
	case DevParam::SET_MOTION_SENSITIVE:   motion->setSensitive(value); break;
	case DevParam::SET_VIDEOCODEC_CURRENT: vCodecIndex = value; break;
	default: break;
}*/
}


//===========================================================================//
//=Заголовок: void VidDevice::slotSetParam() - public slots
//=Параметры:
//=Описание: Слот установки параметра устройства.
//===========================================================================//
void VidDevice::slotSetParam(DevParam::SET_PARAM /*param*/, bool /*value*/)
{
/*switch(param)
	{
	case DevParam::SET_MOTION_ENABLED:       setMotionEnabled(value); break;
	case DevParam::SET_MOTION_TEST_ENABLED:  motion->setTestMode(value); break;
	case DevParam::SET_MOTION_NOISEREMOVING: motion->setNoiseRemoving(value); break;
	case DevParam::SET_FILEWRITER_TRIGGER:   setFileWriterTrigger(value); break;
	default: break;
}*/
}


//===========================================================================//
//=Заголовок: void VidDevice::run() - protected
//=Параметры:
//=Описание: Функция активации нити.
//===========================================================================//
void VidDevice::run()
{
vidStream = new VidStream(0);
connect(this, SIGNAL(sigSetParam(DevParam::SET_PARAM, int)), vidStream, SLOT(slotSetParam(DevParam::SET_PARAM, int)), Qt::DirectConnection);
connect(this, SIGNAL(sigSetParam(DevParam::SET_PARAM, bool)), vidStream, SLOT(slotSetParam(DevParam::SET_PARAM, bool)), Qt::DirectConnection);
connect(this, SIGNAL(sigSetParam(DevParam::SET_PARAM, QObject *)), vidStream, SLOT(slotSetParam(DevParam::SET_PARAM, QObject *)), Qt::DirectConnection);

//Открыть устройство.
if(!openDevice()) return;

//Активировать устройство.
if(!startCapturing()) return;

exec();
}


//===========================================================================//
//=Заголовок: void VidDevice::processVideo() - protected
//=Параметры:
//=Описание: Функция обработки видеокадра.
//===========================================================================//
void VidDevice::processVideo(VidFrame */*vidframe*/, int */*framerate*/)
{
/*if(!vidframe) return;

switch(devMode)
	{
	case VIEW: {
		//Вывести наименование устройства на видеокадр.
		vidframe->drawText(baseName, 10, 15);
	} break;

	case MOTION: {
		//Выполнить детекцию движения.
		if(!motion->isRunning()) motion->detect(vidframe);
		else if(framerate) {if((*framerate) > 1) (*framerate)--;}

		//Нарисовать точки движения на видеокадре.
		if(motion->isTestMode()) motion->drawDetection(vidframe->getCVImage());

		//Вывести наименование устройства на видеокадр.
		vidframe->drawText(baseName, 10, 15);

		//Вывести информацию об активности ДД на видеокадр.
		vidframe->drawText("(M)", vidframe->getWidth() - 30, 15);
	} break;

	case FILEWRITING: {
		//Вывести наименование устройства на видеокадр.
		vidframe->drawText(baseName, 10, 15);

		//Вывести текущее время на видеокадр.
		QDateTime dt = QDateTime::currentDateTime();
		QString dt_str = dt.toString("hh:mm:ss dd.MM.yyyy");
		vidframe->drawText(dt_str, vidframe->getWidth() - 145, vidframe->getHeight() - 10);

		//Запись в файл.
		if(!fileWriter->isRunning()) fileWriter->write(vidframe);
		else if(framerate) {if((*framerate) > 1) (*framerate)--;}

		//Вывести информацию об активности записи на видеокадр.
		vidframe->drawText("(R)", vidframe->getWidth() - 30, 15);
	} break;
}*/
}


//===========================================================================//
//=Заголовок: void VidDevice::setMotionEnabled() - protected
//=Параметры:
//=Описание: Функция управления активностью детектора движения.
//===========================================================================//
void VidDevice::setMotionEnabled(bool enabled)
{
//Сохранить текущий режим работы устройства.
/*MODE devmode = devMode;

//Сменить режим работы устройства.
switch(enabled)
	{
	case true: devMode = MOTION; break;
	case false: devMode = VIEW; break;
}

//Указать, что для записи видео в файл
//используется (или нет) детектор движения.
isMotion = enabled;

//Если предыдущим режимом работы устройства являлась запись видео в файл...
if(devmode == FILEWRITING)
	{
	if(stopWriteTimer) stopWriteTimer->stop();
	if(fileWriter) fileWriter->stopWrite();
}*/
}


//===========================================================================//
//=Заголовок: void VidDevice::setFileWriterTrigger() - private
//=Параметры:
//=Описание: Функция управления активностью "ручной" записи в файл.
//===========================================================================//
void VidDevice::setFileWriterTrigger(bool is_absolute_switch)
{
//Выйти, если включен ДД и отсутствует условие абсолютного
//переключения режима записи в файл.
/*if(!is_absolute_switch && isMotion) return;

//Если включен ДД, то отключить его вместе с таймером остановки записи в файл.
if(isMotion) {isMotion = false; if(stopWriteTimer) stopWriteTimer->stop();}

//Определить, активен ли на данный момент времени режим записи в файл.
switch(devMode)
	{
	case FILEWRITING:
		devMode = VIEW;
		if(fileWriter) fileWriter->stopWrite();
	break;
	default: devMode = FILEWRITING; break;
}*/
}


//===========================================================================//
//=Заголовок: void VidDevice::slotModeTriggered() - private slots
//=Параметры:
//=Описание: Слот смены режима работы устройства.
//===========================================================================//
void VidDevice::slotModeTriggered()
{
/*if(!isMotion) return;

switch(devMode)
	{
	case MOTION:
		devMode = FILEWRITING;
		stopWriteTimer->start();
	break;

	case FILEWRITING:
		devMode = MOTION;
		fileWriter->stopWrite();
	break;

	default: break;
}*/
}
